'''
    pdf.py
    A PDF export plugin for the ActionCal Calendar

    Copyright (C) 2007  R. P. Dillon

    This program is free software; you can redistribute it and/or modify
    it under the terms of version 2 the GNU General Public License as
    published by the Free Software Foundation.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
'''


from reportlab.pdfgen import canvas
from reportlab.lib.pagesizes import landscape, letter
from reportlab.lib.units import inch

from events import *
import datetime
import calendar

class CreatePdfEvent(event):
    """
    An event to activate the PdfCreator plugin.
    """
    pass

class PdfCreator(object):

    def __init__(self, manager):
        """
        Simple initialization method that give this plugin a manager
        to connect it to the rest of the system.
        """

        self.manager = manager
        self.manager.register(self)

        # In addition to simply initialzing, we have to provide a hook
        # for this plugin to be activated.  One way to do this is to
        # bind activation to a key press and have that bound to a
        # function that posts an event that we look for. When we
        # recieve the event, we can take action.
        key = "<Control-p>"
        def postPdfEvent(event):
            self.manager.post(CreatePdfEvent())            
        self.manager.post(KeyBindEvent(key, postPdfEvent))


    def handle(self, event):
        if isinstance(event, monthdataevent):
            self.currentmonth = event.month
            self.currentdata = event.data
        elif isinstance(event, CreatePdfEvent):
            self.manager.post(SyncEvent())
            self.manager.post(requestmonthdataevent(self.currentmonth))
            self.createPdf(self.currentmonth, self.currentdata)

    
    def truncLine(self, text, maxWidth, fontName, fontSize, postText="..."):
        """
        Helper method.
        Returns a line of text that is truncated at the given width (in
        pixels) for the given font name and font size.

        If a line has to be truncated, it will be appended with postText
        to indicate that it has been truncated.
        """

        # This is pretty insane, since there are 4 ways out of the method.
        # I still think it is better than conflating them all to one variable
        # and returning that at the end.
        size = canvas.pdfmetrics.stringWidth(text, fontName, fontSize)
        if size <= maxWidth:
            return text
        else:
            words = text.split()
            lastbest = ""
            for i in range(len(words)):
                current = " ".join(words[:i])
                size = canvas.pdfmetrics.stringWidth(current + postText, fontName, fontSize)
                if size < maxWidth:
                    lastbest = current + postText
                elif size == maxWidth:
                    return current + postText
                else:
                    return lastbest
            # If we close out of the loop and never reach the the limit
            # something has gone wrong:
            print("Check your logic on string: " + text)
            return lastbest

    calendar.setfirstweekday(6)

    def createPdf(self, date, data):
        """
        Creates a pdf of the month associated with the given date and with the data
        supplied as a dictionary of strings values indexed on date keys.
        """

        # Strings
        yearString = date.strftime("%Y")
        monthString = date.strftime("%B")

        #Some calculated dimensions
        pagesize = landscape(letter)
        width, height = pagesize
        leftmargin = .25*inch
        rightmargin = .25*inch
        topmargin = .25*inch
        bottommargin = .25*inch
        titleheight = .5*inch
        numweeks = len(calendar.monthcalendar(date.year, date.month))
        daywidth = (width-leftmargin-rightmargin)/7
        dayheight = (height-topmargin-bottommargin-titleheight)/numweeks

        #Create the document
        c = canvas.Canvas(yearString+monthString+".pdf", pagesize=landscape(letter))

        # Draw the title
        titlefont = "Helvetica"
        titlesize = 24
        c.setFont(titlefont, titlesize)

        title = monthString + " " + yearString
        c.rect(leftmargin, height-titleheight-topmargin, width-leftmargin-rightmargin, titleheight)
        c.drawCentredString(width/2, height-topmargin-(titleheight/2)-(titlesize/2), title)

        #draw the grid
        s = leftmargin
        daylist = [s, s+1*daywidth, s+2*daywidth, s+3*daywidth, s+4*daywidth,\
                 s+5*daywidth, s+6*daywidth, s+7*daywidth]
        s = bottommargin
        weeklist = [s]
        for i in range(numweeks):
            weeklist.append(s+((i+1)*dayheight))
        c.grid(daylist, weeklist)

        #draw day numbers
        daynumfont = "Helvetica"
        daynumsize = 16
        dataFont = "Helvetica"
        dataSize = 10
        cal = calendar.monthcalendar(date.year, date.month)

        #exceptionally unfunctional, apparently python's
        #reverse() is destructive(!)
        weeklist.reverse()

        for week in range(len(cal)):
            for day in range(len(cal[week])):
                if cal[week][day] != 0:
                    c.setFont(daynumfont, daynumsize)
                    s = str(cal[week][day]) #the day number

                    # Draw the day number in the upper right corner of
                    # each day.
                    daynumwidth = c.stringWidth(s, daynumfont, daynumsize)
                    # The magic 5 is an extra horizontal buffer
                    xloc = daylist[day+1]- daynumwidth - 5
                    yloc = weeklist[week] - daynumsize
                    c.drawString(xloc, yloc, s)

                    # Put any text into the day
                    tempDay = datetime.date(date.year, date.month, cal[week][day])
                    if tempDay in data:

                        # Prepare the text region
                        text = c.beginText()
                        text.setFont(dataFont, dataSize)
                        # Magic 6 makes up for magic 5 above, and adds a little
                        # for a (very small) left margin
                        # TODO: Fix all the damn magic numbers!
                        text.setTextOrigin(xloc-(daywidth-daynumwidth-6), yloc-dataSize)

                        # Get our strings
                        lines = data[tempDay].splitlines()
                        #Calculate day box lower boundary.
                        lowerBound = weeklist[week] - dayheight
                        # Now print them to the box (only the ones that fit!)
                        # This tests lines running outside the box by checking
                        # against a lowerbound.
                        for line in lines:
                            textToPrint = self.truncLine(line, daywidth, dataFont, dataSize)
                            text.textLine(textToPrint)
                            if text.getY() < lowerBound:
                                break

                        c.drawText(text)

        # Create the pdf file
        c.showPage()
        c.save()

# This is how a plugin registers with the application
pdfPlugin = PdfCreator(manager)
